package com.ssy.lingxi.member.merchant.serviceimpl.base;

import com.ssy.lingxi.common.constant.basic.EnableDisableStatus;
import com.ssy.lingxi.common.constant.member.MemberLevelTypeEnum;
import com.ssy.lingxi.common.constant.member.MemberStatusEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.utils.DateTimeUtil;
import com.ssy.lingxi.member.merchant.api.constant.MemberLevelRuleTypeEnum;
import com.ssy.lingxi.member.merchant.api.constant.MemberRelationTypeEnum;
import com.ssy.lingxi.member.merchant.config.ServiceConfig;
import com.ssy.lingxi.member.merchant.entity.*;
import com.ssy.lingxi.member.merchant.model.constant.MemberValidateStatusEnum;
import com.ssy.lingxi.member.merchant.repository.*;
import com.ssy.lingxi.member.merchant.service.base.IBaseMemberLevelAsyncService;
import com.ssy.lingxi.member.merchant.service.base.IBaseMemberLrcCacheService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 会员等级相关计算服务异步接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2020-11-17
 */
@Service
public class BaseMemberLevelAsyncServiceImpl implements IBaseMemberLevelAsyncService {
    private static final Logger logger = LoggerFactory.getLogger(BaseMemberLevelAsyncServiceImpl.class);

    @Resource
    private IBaseMemberLrcCacheService lrcCacheService;

    @Resource
    private MemberRelationRepository relationRepository;

    @Resource
    private MemberLevelRuleConfigRepository memberLevelRuleConfigRepository;

    @Resource
    private MemberLevelConfigRepository memberLevelConfigRepository;

    @Resource
    private MemberLevelRightRepository memberLevelRightRepository;

    @Resource
    private MemberLevelHistoryRepository memberLevelHistoryRepository;

    @Resource
    private MemberLoginHistoryRepository memberLoginHistoryRepository;

    /**
     * 用户登录后，计算平台会员升级分
     * @param platformMemberRelationDO 平台会员关系
     * @param loginSourceEnum 登录来源
     */
    @Async
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void calculateMemberLoginScore(MemberRelationDO platformMemberRelationDO, Integer loginSourceEnum) {
        try {
            if(platformMemberRelationDO == null) {
                logger.error("用户登录后，计算平台会员升级分错误: 平台会员关系为Null");
                return;
            }

            if(!platformMemberRelationDO.getVerified().equals(MemberValidateStatusEnum.VERIFY_PASSED.getCode())) {
                return;
            }

            //查询最后一次登录时间
            MemberLoginHistoryDO historyDO = memberLoginHistoryRepository.findFirstByMemberIdAndRoleIdOrderByLoginTimeDesc(platformMemberRelationDO.getSubMemberId(), platformMemberRelationDO.getSubRoleId());
            if(historyDO == null || DateTimeUtil.diffMinutes(historyDO.getLoginTime(), LocalDateTime.now()) >= ServiceConfig.LOGIN_LEVEL_SCORE_CALCULATION_INTERVAL_MINUTES) {
                if(!calculateSubMemberLevelScore(platformMemberRelationDO, MemberLevelRuleTypeEnum.BY_LOGIN, 0D, "")) {
                    return;
                }

                //记录或更新登录时间
                if(historyDO == null) {
                    historyDO = new MemberLoginHistoryDO();
                    historyDO.setMemberId(platformMemberRelationDO.getSubMemberId());
                    historyDO.setRoleId(platformMemberRelationDO.getSubRoleId());
                }

                historyDO.setLoginTime(LocalDateTime.now());
                historyDO.setLoginSource(loginSourceEnum);

                memberLoginHistoryRepository.saveAndFlush(historyDO);
            }

        } catch (Exception e) {
            e.printStackTrace();
            logger.error("用户登录后，计算平台会员升级分", e);
        }
    }

    /**
     * 交易订单完成后，计算买方（下级会员）的等级积分
     * @param upperMemberId 上级会员Id（卖方）
     * @param upperRoleId   上级会员角色Id
     * @param subMemberId   下级会员Id（买方）
     * @param subRoleId     下级会员角色Id
     * @param amount        订单金额
     * @param orderNo       订单号
     */
    @Override
    public void calculateSubMemberTradeScore(Long upperMemberId, Long upperRoleId, Long subMemberId, Long subRoleId, Double amount, String orderNo) {
        try {
            //Step 1: 查询会员关系，如果存在则计算会员等级积分
            MemberRelationDO relationDO = relationRepository.findFirstByMemberIdAndRoleIdAndSubMemberIdAndSubRoleId(upperMemberId, upperRoleId, subMemberId, subRoleId);
            if(relationDO != null && relationDO.getVerified().equals(MemberValidateStatusEnum.VERIFY_PASSED.getCode()) && relationDO.getStatus().equals(MemberStatusEnum.NORMAL.getCode()) && !memberLevelHistoryRepository.existsByMemberIdAndRoleIdAndSubMemberIdAndSubRoleIdAndRemark(relationDO.getMemberId(), relationDO.getRoleId(), relationDO.getSubMemberId(), relationDO.getSubRoleId(), orderNo) && !calculateSubMemberLevelScore(relationDO, MemberLevelRuleTypeEnum.BY_TRADE, amount, orderNo)) {
                return;
            }

            //Step 2: 查询下级会员的平台关系，计算平台等级积分
            MemberRelationDO platformMemberRelationDO = relationRepository.findFirstBySubMemberIdAndSubRoleIdAndRelType(subMemberId, subRoleId, MemberRelationTypeEnum.PLATFORM.getCode());
            if(platformMemberRelationDO == null) {
                logger.error(String.format("计算会员交易等级积分错误，会员Id:%d , 角色Id: %d 没有平台会员关系", subMemberId, subRoleId));
                return;
            }

            if(platformMemberRelationDO.getVerified().equals(MemberValidateStatusEnum.VERIFY_PASSED.getCode()) && platformMemberRelationDO.getStatus().equals(MemberStatusEnum.NORMAL.getCode()) && !memberLevelHistoryRepository.existsByMemberIdAndRoleIdAndSubMemberIdAndSubRoleIdAndRemark(platformMemberRelationDO.getMemberId(), platformMemberRelationDO.getRoleId(), platformMemberRelationDO.getSubMemberId(), platformMemberRelationDO.getSubRoleId(), orderNo)) {
                calculateSubMemberLevelScore(platformMemberRelationDO, MemberLevelRuleTypeEnum.BY_TRADE, amount, orderNo);
            }

        } catch (Exception e) {
            e.printStackTrace();
            logger.error("计算会员交易等级积分错误", e);
        }
    }

    /**
     * 交易评价完成，计算评论方（下级会员）的等级积分
     *
     * @param upperMemberId 上级会员Id（被评论方）
     * @param upperRoleId   上级会员角色Id
     * @param subMemberId   下级会员Id（评论方）
     * @param subRoleId     下级会员角色Id
     * @param orderNo       订单号
     */
    @Async
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public void calculateSubMemberCommentScore(Long upperMemberId, Long upperRoleId, Long subMemberId, Long subRoleId, String orderNo) {
        try {
            //Step 1: 查询会员关系，如果存在则计算会员等级积分
            MemberRelationDO relationDO = relationRepository.findFirstByMemberIdAndRoleIdAndSubMemberIdAndSubRoleId(upperMemberId, upperRoleId, subMemberId, subRoleId);
            if(relationDO != null && relationDO.getVerified().equals(MemberValidateStatusEnum.VERIFY_PASSED.getCode()) && relationDO.getStatus().equals(MemberStatusEnum.NORMAL.getCode()) && !calculateSubMemberLevelScore(relationDO, MemberLevelRuleTypeEnum.BY_COMMENT, 0d, orderNo)) {
                return;
            }

            //Step 2: 查询下级会员的平台关系，计算平台等级积分
            MemberRelationDO platformMemberRelationDO = relationRepository.findFirstBySubMemberIdAndSubRoleIdAndRelType(subMemberId, subRoleId, MemberRelationTypeEnum.PLATFORM.getCode());
            if(platformMemberRelationDO == null) {
                logger.error(String.format("计算会员评价等级积分错误，会员Id:%d , 角色Id: %d 没有平台会员关系", subMemberId, subRoleId));
                return;
            }

            if(platformMemberRelationDO.getVerified().equals(MemberValidateStatusEnum.VERIFY_PASSED.getCode()) && platformMemberRelationDO.getStatus().equals(MemberStatusEnum.NORMAL.getCode())) {
                calculateSubMemberLevelScore(platformMemberRelationDO, MemberLevelRuleTypeEnum.BY_COMMENT, 0d, orderNo);
            }

        } catch (Exception e) {
            e.printStackTrace();
            logger.error("计算会员评价等级积分错误", e);
        }
    }

    /**
     * 计算下级会员的等级积分
     * <p>调用之前，要判断是否具有上下级关系，即MemberRelationDO是否存在，如不存在，不能调用此接口</p>
     * <p>登录积分：amount、orderId可以为Null</p>
     * <p>交易积分：amount、orderId为必须参数</p>
     * <p>评论积分：orderId为必须参数</p>
     * @param relationDO 会员等级关系
     * @param memberLevelRuleTypeEnum 升级规则类型枚举
     * @param amount 订单金额
     * @param orderNo 订单号
     * @return 是否成功
     */
    private Boolean calculateSubMemberLevelScore(MemberRelationDO relationDO, MemberLevelRuleTypeEnum memberLevelRuleTypeEnum, Double amount, String orderNo) {
        //根据状态、等级类型，查询升级规则，如果没有，返回
        Integer memberLevelTypeEnum = relationDO.getRelType().equals(MemberRelationTypeEnum.PLATFORM.getCode()) ? MemberLevelTypeEnum.PLATFORM.getCode() : MemberLevelTypeEnum.getCodeByMemberType(relationDO.getSubRole().getMemberType().getTypeEnum());
        Specification<MemberLevelRuleConfigDO> memberLevelRuleSpec = (Specification<MemberLevelRuleConfigDO>) (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("memberId").as(Long.class), relationDO.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("roleId").as(Long.class), relationDO.getRoleId()));
            list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), EnableDisableStatus.ENABLE.getCode()));

            Join<Object, Object> baseLevelRuleJoin = root.join("rule", JoinType.LEFT);
            list.add(criteriaBuilder.equal(baseLevelRuleJoin.get("memberLevelTypeEnum").as(Integer.class), memberLevelTypeEnum));
            list.add(criteriaBuilder.equal(baseLevelRuleJoin.get("status").as(Integer.class), EnableDisableStatus.ENABLE.getCode()));
            list.add(criteriaBuilder.equal(baseLevelRuleJoin.get("ruleTypeEnum").as(Integer.class), memberLevelRuleTypeEnum.getCode()));
            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        Page<MemberLevelRuleConfigDO> pageList = memberLevelRuleConfigRepository.findAll(memberLevelRuleSpec, PageRequest.of(0, 1));
        if(CollectionUtils.isEmpty(pageList.getContent())) {
            return true;
        }

        //如果没有升级规则，或参数设置小于等于0，不升级
        MemberLevelRuleConfigDO levelRuleConfig = pageList.getContent().get(0);
        if(levelRuleConfig == null || levelRuleConfig.getScore().compareTo(new BigDecimal(0)) <= 0) {
            return true;
        }

        //下级会员的当前等级信息
        //这里有判断，即：在系统初始化的时候，如果配置了等级权益信用等数据，levelRightDO不能为Null
        MemberLevelRightDO levelRightDO = relationDO.getLevelRight();
        if (levelRightDO == null) {
            logger.error("计算" + memberLevelRuleTypeEnum.getMessage()  + "升级分： 会员RelationId:" + relationDO.getId() + "没有当前等级数据");
            return false;
        }

        //下级会员当前的等级
        Integer currentLevel = levelRightDO.getLevel();
        String currentLevelTag = levelRightDO.getLevelTag();

        //根据上级会员、下级会员等级类型、下级会员角色Id，查询等级配置，如果没有，返回
        Specification<MemberLevelConfigDO> specification = (Specification<MemberLevelConfigDO>) (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("memberId").as(Long.class), relationDO.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("roleId").as(Long.class), relationDO.getRoleId()));
            list.add(criteriaBuilder.greaterThan(root.get("point").as(Integer.class), 0));
            list.add(criteriaBuilder.equal(root.get("subRoleId").as(Long.class), relationDO.getSubRole().getId()));
            list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), EnableDisableStatus.ENABLE.getCode()));
            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        List<MemberLevelConfigDO> memberLevelConfigDOList = memberLevelConfigRepository.findAll(specification);
        if (CollectionUtils.isEmpty(memberLevelConfigDOList)) {
            return true;
        }

        //根据下级会员等级、下级会员角色 查询当前配置，如果没有、或升级阈值小于等于0、或状态为无效，返回
        Integer finalCurrentLevel = currentLevel;
        MemberLevelConfigDO memberLevelConfigDO = memberLevelConfigDOList.stream().filter(configDO -> configDO.getLevel().equals(finalCurrentLevel)).findFirst().orElse(null);
        if (memberLevelConfigDO == null || memberLevelConfigDO.getPoint().equals(0) || memberLevelConfigDO.getStatus().equals(EnableDisableStatus.DISABLE.getCode())) {
            return true;
        }

        //Step 1: 计算等级，如果没有积分权益或升级阈值为0则不计算
        //升级阈值
        Integer levelUpPoint = memberLevelConfigDO.getPoint();
        //当前的积分
        Integer currentScore = levelRightDO.getScore();
        //获得的积分：登录、评价为配置的积分，交易积分要乘以交易金额
        Integer plusScore;
        if(memberLevelRuleTypeEnum.getCode().equals(MemberLevelRuleTypeEnum.BY_LOGIN.getCode()) || memberLevelRuleTypeEnum.getCode().equals(MemberLevelRuleTypeEnum.BY_COMMENT.getCode())) {
            plusScore = levelRuleConfig.getScore().intValue();
        } else {
            plusScore = levelRuleConfig.getScore().multiply(new BigDecimal(amount)).intValue();
        }

        //如果得分大于0，且总积分大于等于升级阈值，升到下一级（可跳级升级）
        if (plusScore > 0 && ((currentScore + plusScore) >= levelUpPoint)) {
            //查找更高的等级
            List<MemberLevelConfigDO> higherLevelConfigList = memberLevelConfigDOList.stream().filter(configDO -> configDO.getLevel() > finalCurrentLevel).collect(Collectors.toList());

            //如果存在更高等级
            if(!CollectionUtils.isEmpty(higherLevelConfigList)) {
                //first : 查询 升级阈值 > 总积分 的最小等级
                //如果 first 为空，查找 升级阈值 <= 总积分的最大等级
                MemberLevelConfigDO nextLevelConfigDO = higherLevelConfigList.stream().filter(configDO -> configDO.getPoint() > (currentScore + plusScore)).min(Comparator.comparingInt(MemberLevelConfigDO::getLevel)).orElse(null);
                if(nextLevelConfigDO == null) {
                    nextLevelConfigDO = higherLevelConfigList.stream().filter(configDO -> configDO.getPoint() <= (currentScore + plusScore)).max(Comparator.comparingInt(MemberLevelConfigDO::getLevel)).orElse(null);
                }

                if (nextLevelConfigDO != null) {
                    currentLevel = nextLevelConfigDO.getLevel();
                    currentLevelTag = nextLevelConfigDO.getLevelTag();
                    //这里需要配置：spring.jpa.properties.hibernate.event.merge.entity_copy_observer=allow
                    levelRightDO.setLevelConfig(nextLevelConfigDO);
                }
            }
        }

        levelRightDO.setLevel(currentLevel);
        levelRightDO.setLevelTag(currentLevelTag);
        levelRightDO.setScore(currentScore + plusScore);
        memberLevelRightRepository.saveAndFlush(levelRightDO);

        //Step 2: 积分获取记录
        MemberLevelHistoryDO levelHistoryDO = new MemberLevelHistoryDO();
        levelHistoryDO.setCreateTime(LocalDateTime.now());
        levelHistoryDO.setMemberId(relationDO.getMemberId());
        levelHistoryDO.setRoleId(relationDO.getRoleId());
        levelHistoryDO.setSubMemberId(relationDO.getSubMemberId());
        levelHistoryDO.setSubRoleId(relationDO.getSubRoleId());
        levelHistoryDO.setRelType(relationDO.getRelType());
        levelHistoryDO.setLevelRuleTypeEnum(memberLevelRuleTypeEnum.getCode());
        levelHistoryDO.setScore(plusScore);
        levelHistoryDO.setRemark(StringUtils.hasLength(orderNo) ? orderNo : "");
        memberLevelHistoryRepository.saveAndFlush(levelHistoryDO);

        //Step 3:缓存平台会员等级信息
        if(relationDO.getRelType().equals(MemberRelationTypeEnum.PLATFORM.getCode())) {
            lrcCacheService.cacheMemberLevel(relationDO.getSubMemberId(), relationDO.getSubRoleId(), levelRightDO.getLevel(), levelRightDO.getLevelTag(), levelRightDO.getScore());
        }

        return true;
    }
}
